#ifndef _TYPELIST_H_
#define _TYPELIST_H_

namespace Medved
{

template <class H, class T>
struct TypeList
{
	typedef H Head;
	typedef T Tail;
};

struct NullType {};

#define TYPELIST_1(T1) TypeList<T1, NullType>
#define TYPELIST_2(T1, T2) TypeList<T1, TYPELIST_1(T2) >
#define TYPELIST_3(T1, T2, T3) TypeList<T1, TYPELIST_2(T2, T3) >
#define TYPELIST_4(T1, T2, T3, T4) TypeList<T1, TYPELIST_3(T2, T3, T4) >
#define TYPELIST_5(T1, T2, T3, T4, T5) TypeList<T1, TYPELIST_4(T2, T3, T4, T5) >
#define TYPELIST_6(T1, T2, T3, T4, T5, T6) TypeList<T1, TYPELIST_5(T2, T3, T4, T5, T6) >
#define TYPELIST_7(T1, T2, T3, T4, T5, T6, T7) TypeList<T1, TYPELIST_6(T2, T3, T4, T5, T6, T7) >
#define TYPELIST_8(T1, T2, T3, T4, T5, T6, T7, T8) TypeList<T1, TYPELIST_7(T2, T3, T4, T5, T6, T7, T8) >


template <class List, int pos>
struct TypeAt;

template <class Head, class Tail, int pos>
struct TypeAt<TypeList<Head, Tail>, pos>
{
	typedef typename TypeAt<Tail, pos-1>::Result Result;
};

template <class Head, class Tail>
struct TypeAt<TypeList<Head, Tail>, 0>
{
	typedef Head Result;
};

template <int pos>
struct TypeAt<NullType, pos>
{
	typedef NullType Result;
};

template <class List, class Type>
struct IsInList;

template <class Head, class Tail, class Type>
struct IsInList<TypeList<Head, Tail>, Type>
{
	enum {Result = IsInList<Tail, Type>::Result };	
};

template <class Type>
struct IsInList<NullType, Type>
{
	enum {Result = false};
};

template <class Tail, class Type>
struct IsInList<TypeList<Type, Tail>, Type>
{
	enum {Result = true};
};

template <class List>
struct Length;

template <class Head, class Tail>
struct Length<TypeList<Head, Tail> >
{
	enum {Result = 1 + Length<Tail>::Result };
};

template <>
struct Length<NullType>
{
	enum { Result = 0 };
};

template <class T1 = NullType, class T2 = NullType, class T3 = NullType, class T4 = NullType, class T5 = NullType,
		class T6 = NullType, class T7 = NullType, class T8 = NullType, class T9 = NullType, class T10 = NullType>
struct Seq
{
	typedef T1 Type1;
	typedef T2 Type2;
	typedef T3 Type3;
	typedef T4 Type4;
	typedef T5 Type5;
	typedef T6 Type6;
	typedef T7 Type7;
	typedef T8 Type8;
	typedef T9 Type9;
	typedef T10 Type10;
	typedef Seq<T2, T3, T4, T5, T6, T7, T8, T9, T10> Tail;
};

template <class Seq>
struct Seq2TypeList
{
	typedef TypeList<typename Seq::Type1, typename Seq2TypeList<typename Seq::Tail>::Result> Result;
};

template <>
struct Seq2TypeList<Seq<NullType, NullType, NullType, NullType> >
{
	typedef NullType Result;
};

}

#endif
